<script lang="ts">
export default {
  name: 'Study4StuDemo5'
}
</script>

<template>
  <h2>响应式 API:工具函数</h2>
  <div class="note">
    <p>1. isRef():检查某个值是否为 ref</p>
    <p>2. unref():卸载ref包装</p>
    <p>  (1) 浅层ref卸载后，返回的普通值；深层ref卸载后，返回的Proxy对象</p>
    <p>3. toRef():将响应式对象中的某个属性转为 ref</p>
    <p>  (1) 只会转化最外层，不会转化子属性</p>
    <p>  (2) 可以基于响应式对象上的一个属性，创建一个对应的 ref。这样创建的 ref 与其源属性保持同步</p>
    <p>4. toRefs(): reactive 将响应式对象中的所有属性转为 ref</p>
    <p>  (1) 注意：toRefs(),如果本身是ref,不会自动解包，需要用.value解包，才能将子属性转换为ref</p>
  </div>
</template>

<script setup lang="ts">
import { isRef, reactive, ref, toRef, toRefs, unref, type Ref } from 'vue'

let numC = 0
let dataNumC: Ref<number> = ref(0)
let dataStr = ref<string>('鸿蒙开发')
let dataBool = ref<boolean>(true)

interface Iuser {
  name: string
  sex: string
  age: number
}

interface IdataObj1<T> {
  count: number
  strArr: string[]
  user: T
}

// 深层响应性 ref
let dataObj1Ref = ref<IdataObj1<Iuser>>({
  count: 0,
  strArr: ['php', '刘强东', '马云', '马化腾', '任正非'],
  user: {
    name: '许家印',
    sex: 'nan',
    age: 42
  }
})

// reactive
let dataObj2Reac: IdataObj1<Iuser> = reactive({
  count: 0,
  strArr: ['php', '刘强东', '马云', '马化腾', '任正非'],
  user: {
    name: '许家印',
    sex: 'nan',
    age: 42,
    aihao: {}
  }
})

// 普通
let dataObj3Com = {
  count: 0,
  strArr: ['php', '刘强东', '马云', '马化腾', '任正非'],
  user: {
    name: '许家印',
    sex: 'nan',
    age: 42,
    aihao: {}
  }
}

// isRef():检查某个值是否为 ref
// 一层ref
console.log(isRef(numC), 'isRef(numC)') // 普通 false
console.log(isRef(dataNumC), 'isRef(dataNumC)') // ref true
console.log(isRef(dataObj1Ref), 'isRef(dataObj1Ref)') // ref true
console.log(isRef(dataObj2Reac), 'isRef(dataObj2Reac)') // reactive false

// unref():卸载ref包装
// 浅层ref卸载后，返回的普通值；深层ref卸载后，返回的Proxy对象
console.log(unref(numC), 'unref(numC)');  // 普通 0
console.log(unref(dataNumC), 'unref(dataNumC)')  // ref 0
console.log(unref(dataObj1Ref), 'unref(dataObj1Ref)')  // ref Proxy
console.log(unref(dataObj2Reac), 'unref(dataObj2Reac)') // reactive Proxy
// unref原理
function myUnref(data: any) {
  return isRef(data) ? data.value : data
}
console.log(myUnref(dataNumC), 'myUnref(dataNumC)')  // ref 0

// toRef():将响应式对象中的某个属性转为 ref
console.log(toRef(numC),'toRef(numC)');  // 普通  ref
console.log(toRef(dataNumC), 'toRef(dataNumC)') // ref ref
console.log(toRef(dataObj1Ref), 'toRef(dataObj1Ref)') // ref ref
console.log(toRef(dataObj2Reac), 'toRef(dataObj2Reac)') // reactive ref
console.log(toRef(dataObj3Com), 'toRef(dataObj3Com)'); // 普通 ref

//判断子属性 
// toRef():只会转化最外层，不会转化子属性
let a = toRef(dataObj1Ref)
let b = toRef(dataObj2Reac)
let c = toRef(dataObj3Com)
console.log(isRef(a.value.count), 'isRef(a.value.count)');   // false
console.log(isRef(b.value.strArr), 'isRef(b.value.strArr)');  // false
console.log(isRef(c.value.user), 'isRef(c.value.user)');  // false

let newdataObj3Com: any = {}
for(const key in dataObj3Com){
  if(Object.prototype.hasOwnProperty.call(dataObj3Com, key)){
    newdataObj3Com[key as keyof typeof dataObj3Com] = toRef(dataObj3Com[key as keyof typeof dataObj3Com])
  } 
}
console.log(newdataObj3Com.count, 'newdataObj3Com.count')   // ref
console.log(newdataObj3Com.strArr, 'newdataObj3Com.strArr')  // ref
console.log(newdataObj3Com.user, 'newdataObj3Com.user')   // ref

console.log(isRef(newdataObj3Com.count), 'isRef(newdataObj3Com.count)')   // true
console.log(isRef(newdataObj3Com.strArr), 'isRef(newdataObj3Com.strArr')  // true
console.log(isRef(newdataObj3Com.user), 'isRef(newdataObj3Com.user')   // true

// 创建一个只读的 ref，当访问 .value 时会调用此 getter 函数
let newRefCount = toRef(()=>{
  return dataObj3Com.count
})
console.log(newRefCount.value, 'newRefCount.value');

// 按原样返回现有的 ref
let dataObj1Ref2 = toRef(dataObj1Ref)
console.log(dataObj1Ref2.value, 'dataObj1Ref2.value');

// 也可以基于响应式对象上的一个属性，创建一个对应的 ref。这样创建的 ref 与其源属性保持同步：
// 改变源属性的值将更新 ref 的值，反之亦然。
const state = reactive({
  foo: 1,
  bar: 2
})

// 双向 ref，会与源属性同步
const fooRef = toRef(state, 'foo')
fooRef.value = 300
console.log(state.foo, 'state.foo');       // 300
console.log(fooRef.value, 'fooRef.value'); // 300
state.foo = 400
console.log(state.foo, 'state.foo');  // 400
console.log(fooRef.value, 'fooRef.value'); // 400



// toRefs(): reactive 将响应式对象中的所有属性转为 ref
// ********注意：toRefs(),如果本身是ref,不会自动解包，需要用.value解包，才能将子属性转换为ref
let d = toRefs(dataObj1Ref.value)
let e = toRefs(dataObj2Reac)
let f = toRefs(dataObj3Com)
console.log(isRef(d.count));  // true
console.log(isRef(e.strArr));  // true
console.log(isRef(f.user));  // true

const stateAsRefs = toRefs(state)
console.log(stateAsRefs.foo, 'stateAsRefs.foo');  // ref
console.log(stateAsRefs.bar, 'stateAsRefs.bar');  // ref
console.log(stateAsRefs.foo.value, 'stateAsRefs.foo.value');  // 400
console.log(stateAsRefs.bar.value, 'stateAsRefs.bar.value');  // 2

// toRefs() 使得解构之后依然有响应
const { foo, bar} = stateAsRefs
console.log(foo,bar);  // ref ref




</script>

<style scoped>
body {
  padding: 20px;
}
h2 {
  color: red;
}
h3 {
  color: rgb(220, 85, 85);
}
h4 {
  color: skyblue;
}
.hr-style {
  border: 3px solid gold;
  margin: 20px 0;
}
.note {
  margin: 10px;
  padding: 10px;
  border: 3px solid rgb(221, 121, 202);
}
.note p {
  color: rgb(226, 106, 204);
  font-weight: bold;
}
</style>
